package us.versus.them.phystickman;

class DistanceJoint extends phx.joint.Joint {
	var distance:Float;

	public function new( b1:phx.Body, b2:phx.Body ) {
		super( b1, b2, null, null );
		this.distance = dist( b1, b2 );
	}

	public function dist( b1:phx.Body, b2:phx.Body ) {
		return Math.sqrt( 
			( b2.x - b1.x ) * ( b2.x - b1.x )
			+
			( b2.y - b1.y ) * ( b2.y - b1.y )
		);
	}

	public override function preStep( invDt : Float ) {
		var currentDistance = dist( b1, b2 );
		if ( 0 != currentDistance ) {

			var diff = ( currentDistance - this.distance ) / currentDistance;
			var xdiff = ( b2.x - b1.x ) * diff;
			var ydiff = ( b2.y - b1.y ) * diff;

			// TODO: use mass

			b1.v.x += xdiff * 0.5; 
			b2.v.x -= xdiff * 0.5;

			b1.v.y += ydiff * 0.5;
			b2.v.y -= ydiff * 0.5;
		}
	}

	public override function applyImpuse() {
	}

	public function draw( graphics:flash.display.Graphics ) {
		graphics.lineStyle( 2, 0 );
		graphics.moveTo( b1.x, b1.y );
		graphics.lineTo( b2.x, b2.y );
	}
}

class App {
	var root : flash.display.MovieClip;
	var world : phx.World;

	var joints : Array< DistanceJoint >;

	public function new() {
		this.root = flash.Lib.current;

		var w = Math.floor( root.stage.stageWidth  * 2 / 3 );
		var h = Math.floor( root.stage.stageHeight * 2 / 3 );

		root.addEventListener( flash.events.Event.ENTER_FRAME, update );
		root.stage.addEventListener( flash.events.MouseEvent.MOUSE_DOWN, copyCat );

		floor( w, h );

		joints = new Array< DistanceJoint >();
		stickman();
/*

		var head = body( 50, 40 );
		var neck = body( 50, 60 );
		var l_shoulder = body( 40, 70 );
		var r_shoulder = body( 60, 70 );
		var l_elbow = body( 30, 90 );
		var r_elbow = body( 70, 90 );
		var l_wrist = body( 30, 120 );
		var r_wrist = body( 70, 120 );
		var navel = body( 50, 100 );
		var l_hip = body( 42, 120 );
		var r_hip = body( 58, 120 );
		var l_knee = body( 42, 140 );
		var r_knee = body( 58, 140 );
		var l_ankle = body( 42, 160 );
		var r_ankle = body( 58, 160 );

		joint( head, neck );
		joint( neck, l_shoulder );
		joint( neck, r_shoulder );
		joint( l_shoulder, r_shoulder );
		joint( l_shoulder, navel );
		joint( r_shoulder, navel );

		joint( l_shoulder, l_elbow );
		joint( r_shoulder, r_elbow );
		joint( l_elbow, l_wrist );
		joint( r_elbow, r_wrist );

		joint( navel, l_hip );
		joint( navel, r_hip );
		joint( l_hip, l_knee );
		joint( l_knee, l_ankle );
		joint( r_hip, r_knee );
		joint( r_knee, r_ankle );
*/
		/*
		// demo the difference between jointed bodies and 
		// body made of multiple shapes
		var q = new phx.Body( 100, 100 );
		q.addShape( phx.Shape.makeBox( 20, 20, 50, 40 ) );
		q.addShape( phx.Shape.makeBox( 20, 20, 50, 140 ) );
		q.addShape( phx.Shape.makeBox( 20, 20, 120, 90 ) );
		world.addBody( q );
		*/
	}

	public function stickman() {
		var f = 6;
		var x = 40;
		var y = 40;
		var head           = body( x + 415.40612 / f, y + 68.16921 / f );
		var neck           = body( x + 415.40612 / f, y + 161.9018 / f );
		var lt_neck        = body( x + 400.40612 / f, y + 191.9018 / f );
		var rt_neck        = body( x + 430.40612 / f, y + 191.9018 / f );
		var lt_shoulder    = body( x + 311.02202 / f, y + 200.2470 / f );
		var lt_elbow       = body( x + 264.15569 / f, y + 313.1523 / f );
		var lt_wrist       = body( x + 238.59224 / f, y + 430.3181 / f );
		var lt_finger_tips = body( x + 236.46195 / f, y + 489.9662 / f );
		var rt_shoulder    = body( x + 519.79022 / f, y + 200.2470 / f );
		var rt_elbow       = body( x + 566.65655 / f, y + 313.1523 / f );
		var rt_wrist       = body( x + 592.22000 / f, y + 430.3181 / f );
		var rt_finger_tips = body( x + 594.35029 / f, y + 489.9662 / f );
		var rib            = body( x + 415.40612 / f, y + 300.9018 / f );
		var back           = body( x + 415.40612 / f, y + 451.9018 / f );
		var lt_hip         = body( x + 330.02202 / f, y + 510.2470 / f );
		var lt_knee        = body( x + 264.15569 / f, y + 613.1523 / f );
		var lt_ankle       = body( x + 238.59224 / f, y + 730.3181 / f );
		var lt_tip_toe     = body( x + 198.46195 / f, y + 730.9662 / f );
		var rt_hip         = body( x + 500.79022 / f, y + 510.2470 / f );
		var rt_knee        = body( x + 566.65655 / f, y + 613.1523 / f );
		var rt_ankle       = body( x + 592.22000 / f, y + 730.3181 / f );
		var rt_tip_toe     = body( x + 632.35029 / f, y + 730.9662 / f );

		joint( head, neck );

		// try to make the neck more stable!
		joint( neck, lt_neck );
		joint( neck, rt_neck );
		joint( head, lt_neck );
		joint( head, rt_neck );
joint( head, back );
		joint( lt_neck, rt_neck );
		joint( lt_neck, lt_shoulder );
		joint( rt_neck, rt_shoulder );

		joint( neck, lt_shoulder );
		joint( lt_shoulder, lt_elbow );
		joint( lt_elbow, lt_wrist );
		joint( lt_wrist, lt_finger_tips  );

		joint( lt_shoulder, rt_shoulder );

		joint( neck, rt_shoulder );
		joint( rt_shoulder, rt_elbow );
		joint( rt_elbow, rt_wrist );
		joint( rt_wrist, rt_finger_tips );

		joint( neck, rib );
		joint( rib, lt_shoulder );
		joint( rib, rt_shoulder );
		joint( rib, back );

		joint( back     , lt_hip     );
		joint( lt_hip   , lt_knee    );
		joint( lt_knee  , lt_ankle   );
		joint( lt_ankle , lt_tip_toe );

		joint( lt_hip, rt_hip );

		joint( back     , rt_hip     );
		joint( rt_hip   , rt_knee    );
		joint( rt_knee  , rt_ankle   );
		joint( rt_ankle , rt_tip_toe );

		// make torso more substantial
		joint( back, rt_shoulder );
		joint( back, lt_shoulder );

		// make the pelvice more substantial
		joint( rib, rt_hip );
		joint( rib, lt_hip );

		// beef up the legs
		joint( back, rt_knee );
		joint( back, lt_knee );
		joint( rt_hip, rt_ankle );
		joint( lt_hip, lt_ankle );
		joint( rt_knee, rt_tip_toe );
		joint( lt_knee, lt_tip_toe );

		// arms
		joint( lt_neck, lt_elbow );
		joint( lt_elbow, lt_finger_tips );
		joint( rt_neck, rt_elbow );
		joint( rt_elbow, rt_finger_tips );
	}

	public function joint( a:phx.Body, b:phx.Body ) {
		var j = new DistanceJoint( a, b );
		world.addJoint( j );
		joints.push( j ); 
	}

	public function body( x,y ) {
		var body = new phx.Body( x, y );
		body.addShape( phx.Shape.makeBox( 10, 10 ) );
		world.addBody( body );
		return body;
	}

	public function floor( w:Int, h:Int ) {
		world = new phx.World( new phx.col.AABB( 0,0, w, h ),new phx.col.SortedList() );
		var floor = new phx.Body(0,0);
		floor.addShape(phx.Shape.makeBox(w,30,0,0));
		floor.addShape(phx.Shape.makeBox(w,30,0,h-30));
		floor.addShape(phx.Shape.makeBox(30,h,0,0));
		floor.addShape(phx.Shape.makeBox(30,h,w-30,0));
		floor.isStatic = true;
		world.addBody(floor);
	}

	public function copyCat( _ ) {
		var w = Math.floor( root.stage.stageWidth  * 2 / 3 );
		var h = Math.floor( root.stage.stageHeight * 2 / 3 );

		var mat = new phx.Material(0,0.3,1);
		for( i in 0...50 ) {
			var points = new Array();
			var size = (6 + Std.random(20)) * 0.7;
			var a = 2 * Math.PI;
			do {
				points.push(new phx.Vector(Math.cos(a) * size,Math.sin(a) * size));
				a -= (Std.random(20) + 5) * 0.1;
			} while( a > 0 );
			var shape = new phx.Body(Std.random(w - 100) + 50,Std.random(h - 100) + 50);
			shape.addShape(new phx.Polygon(points,new phx.Vector(0,0),mat));
			world.addBody(shape);
		}
	}

	function update( _ ) {
		var w = Math.floor( root.stage.stageWidth  * 2 / 3 );
		var h = Math.floor( root.stage.stageHeight * 2 / 3 );

		var xf = (root.mouseX / w) * 2 - 1;
		var yf = (root.mouseY / h) * 2 - 1;
		world.gravity.x = xf * 0.5;
		world.gravity.y = yf * 0.5;

		// TODO: hook in the joints the right way...
		for ( j in joints ) j.preStep( 0 );
		world.step(1.0,20);

		root.graphics.clear();

		var d = new phx.FlashDraw(root.graphics);
		d.drawWorld(world);

		for ( j in joints ) {
			j.draw( root.graphics );
		}
	}

}
